"use strict";

(function(host){

//全局ID
var ID = 0;

var CONFIGURE = {
	prefix: 'k',
	namespace: 'mkjs'
};

function prefixed(n){
	return CONFIGURE.prefix ? CONFIGURE.prefix + '-' + n : n;
}
function createElement(conf){
	var el = document.createElement(conf.name || 'div');
	if(conf.className && util.isString(conf.className)){
		el.className = conf.className;
	}
	if(conf.text && util.isString(conf.text)){
		el.innerHTML = conf.text;
	}
	util.isObject(conf.attrs) && util.each(conf.attrs, function(v, p){
		if(p === 'className' || p === 'class') return;
		el.setAttribute(p, v);
	});
	return el;
};

function Message(){
	this.listeners = {};
}
Message.prototype = {
	$has: function(name){
		return name in this.listeners;
	},
	add: function(name, listener, context){
		if(!this.$has(name)){
			this.listeners[name] = [];
		}
		this.listeners[name].push([listener, context]);
		return this;
	},
	remove: function(name, fn){
		if(this.$has(name)){
			// console.log(this.listeners[name].length)
			this.listeners[name] = this.listeners[name].filter(function(tar){
				return !fn(tar);
				// return tar[0] != listener;
			});
			// console.log(this.listeners[name].length)
		}
		return this;
	},
	tell: function(name){
		if(!this.$has(name)) return;
		var args = Array.prototype.slice.call(arguments, 0);
		args.unshift(null);
		util.EACH(this.listeners[name], function(tar){
			args[0] = tar[1];
			if(util.isFunction(args[0])){
				args[0] = args[0]();
			}
			return tar[0].listen.apply(tar[0], args);
		});
		return this;
	}
};

function Listener(schema){
	if(util.isArray(schema)){
		this.schema = schema;
	} else {
		this.schema = Array.prototype.slice.call(arguments, 0);
	}
	this.schema = this.schema.filter(function(a){
		return typeof(a) == 'string';
	})
}
Listener.prototype = {
	listen: function(){
		console.log('listening...', arguments);
		return !1;
	}
};

//Model
function UIModel(obj){
	this.id = ++ID;
	this.data = {};
	this.getter = {};
	this.message = new Message();
	this.__init(obj);
}
UIModel.prototype = {
	__init: function(obj){
		if(!util.isObject(obj)) return this;
		var _ = this;
		util.each(obj, function(v, p){
			if(/^[a-z\-]/i.test(p)){
				_.set(p, v);
			}
		});
		return this;
	},
	clone: function(){
		var m = new UIModel(this.data);
		util.each(this.getter, function(v, p){
			m.defineGetter(p, v);
		});
		return m;
	},
	output: function(){
		var data = {}, _ = this;
		this.each(function(v, p){
			data[p] = v;
		});
		return data;
	},
	has: function(key){
		return key in this.data;
	},
	get: function(key){
		if(util.isString(key)){
			if(arguments.length < 2){
				if(util.isFunction(this.getter[key])){
					return this.getter[key](this.data[key], key);
				}
				return this.data[key];
			} else {
				return this.get(util.slice(arguments));
			}
		} else if(util.isArray(key)){
			return this.map(key);
		} else {
			return this.get(key + '');
		}
		
	},
	set: function(key, value, noTell){

		if(util.isObject(key)){
			var _ = this, schema = util.isObject(noTell) ? noTell : {};
			util.each(key, function(v, k){
				if(k in schema) k = schema[k];
				_.set(k, v, value);
			});
			return;
		}

		if(!this.has(key)){
			this.data[key] = undefined;
		}
		var val = this.get(key);
		if(util.isFunction(this.getter[key])){
			value = this.getter[key](value, key);
		}
		if(val === value) return;
		this.data[key] = value;
		!noTell && this.message.tell(key, value, val);
	},
	bind: function(key, listener, context){
		if(util.isString(key)){
			listener.listen(context, key, this.get(key));
			this.message.add(key, listener, context);
		} else if(util.isArray(key)){
			var _ = this;
			util.EACH(key, function(k){
				_.bind(k, listener, context);
			});
		}
		return this;
	},
	unbind: function(key, fn){
		if(util.isString(key)){
			this.message.remove(key, fn);
		} else if(util.isArray(key)){
			var _ = this;
			util.EACH(key, function(k){
				_.unbind(k, fn);
			});
		}
		return this;
	},
	each: function(fn){
		for(var p in this.data){
			if(fn(this.get(p), p, this.data)) return;
		}
	},
	defineGetter: function(key, fn){
		if(util.isArray(key)){
			var _ = this;
			key.forEach(function(k){
				_.defineGetter(k, fn);
			})
		} else {
			this.getter[key] = fn;
		}
	},
	getKeys: function(){
		var keys = [];
		this.each(function(v, p){
			/^[a-z]/i.test(p) && keys.push(p);
		});
		return keys;
	},
	map: function(keys){
		var o = {}, _ = this;
		keys.forEach(function(key){
			o[key] = _.get(key);
		});
		return o;
	}
};

//Controller
var Controller = util.Class(
	'Controller',
	Listener);


//Controller
var UISchemaController = util.Class(
	'UISchemaController',
	Controller);

//扩展将Model以type作为NS写入／获取DOM的Attribute
var UIAttributeNSController = util.Class(
	'UIAttributeNSController',
	UISchemaController,
	function(){},
	{
		WriteToView: function(model, view){
			var data = model.map(this.schema);
			util.each(data, function(v, p){
				var ns = util.serializableTypeNS(v);
				if(ns){
					view.element.setAttributeNS(ns, prefixed(p), v);
				}
			});
		},
		GetFromView: function(view){
			var o = {};
			this.schema.forEach(function(key){
				var v = view.element.attributes.getNamedItem(prefixed(key));
				if(v && v.namespaceURI){
					v = util.parseValue(v.value, v.namespaceURI);
					if(v === null) return;
					o[key] = v;
				}
			})
			return o;
		}
	}
	);

//扩展将Model写入style
var UICSSController = util.Class(
	'UICSSController',
	UISchemaController,
	function(){},
	{
		update: function(model, view){
			var data = model.map(this.schema);
			view.css(data);
		},
		updateStyle: function(model, style){
			var data = model.map(this.schema);
			util.obj2style(style, data);
		},
		listen: function(style, key, val){
			// var css = util.obj2css(key, val);
			util.obj2style(style, key, val);
		}
	}
	);

var UIInputController = util.Class(
	'UIInputController',
	Listener,
	function(){},
	{
		listen: function(input, key, value){
			input.val(value);
		}
	}
	);


//View
function UIView(el, type, id){
	this.type = type || 'layer';
	// this.prefix = CONFIGURE.prefix;
	// this.namespace = CONFIGURE.namespace;
	this.__isSub = typeof(id) == 'number' && id > 0;
	this.__id = this.__isSub ? id : ++ID;
	this.__init(el);
}
UIView.prototype = {
	__init: function(el){
		if(util.isObject(el)){
			el = MVC.createElement(el);
		} else if(util.isString(el) && el){
			el = util.$(el);
		}
		this.element = el || document.createElement('div');
		this.attr('id', this.$id())
			.attr('type', this.type);
		if(!this.__isSub){
			this.attr('ns', CONFIGURE.namespace, 1)
				// .addClass(this.$getCssName())
				;
		}
	},
	$id: function(){ return this.__id; },
	$getCssName: function(){ return '_cls_' + this.$id(); },
	addClass: function(clsName){
		this.element.classList.add(clsName);
		return this;
	},
	removeClass: function(clsName){
		this.element.classList.remove(clsName);
		return this;
	},
	clearClass: function(clearSelf){
		var _ = this, _cls_ = _.$getCssName();
		this.element.classList.forEach(function(n){
			if(n === _cls_ && !clearSelf) return;
			_.removeClass(n);
		});
		_ = null;
		return this;
	},
	text: function(v){
		if(arguments.length > 0){
			var S = '';
			util.EACH(arguments, function(s){
				S += s + '';
			})
			this.element.innerHTML = S;
			return this;
		} else {
			return this.element.innerHTML;
		}
	},
	addText: function(){
		if(arguments.length > 0){
			var S = '';
			util.EACH(arguments, function(s){
				S += s + '';
			})
			this.element.innerHTML += S;
		}
		return this;
	},
	css: function(o, v){
		this.element.style.cssText = util.obj2css(o, v);
		return this;
	},
	attr: function(name, value, noPrefix){
		if(!name) return null;
		if(!noPrefix){
			name = CONFIGURE.prefix ? (CONFIGURE.prefix + '-' + name) : name;
		}
		if(arguments.length < 2){
			return this.element.getAttribute(name);
		}
		this.element.setAttribute(name, value);
		return this;
	},
	append: function(el){
		if(arguments.length == 1){
			if(typeof(el) == 'string'){
				this.element.innerHTML = el;
			} else if(el instanceof Element){
				this.element.appendChild(el);
			} else if(el instanceof UIView || ('element' in el)){
				this.append(el.element);
			}
		} else {
			var _ = this;
			util.EACH(arguments, function(arg){
				_.append(arg);
			});
		}
		return this;
	},
	appendTo: function(el){
		if(el instanceof Element){
			el.appendChild(this.element);
		} else if(typeof(el) == 'string'){
			var p = util.$(el);
			p && p.appendChild(this.element);
		} else if(el instanceof UIView){
			this.appendTo(el.element);
		}

		return this;
	}
};

var UIInputView = util.Class('UIInputView', UIView, function(){}, {
	__init: function(el){
		var _ = this;
		UIView.prototype.__init.apply(_, arguments);
		util.addEvent(_.element, 'input', function(e){
			e.stopPropagation();
			util.isFunction(_.oninput) && _.oninput(_.val());
		});
	},
	val: function(v){
		if(arguments.length < 1){
			var result = this.element.value;
			switch(this.element.type){
				case 'number':
				case 'range':
					result = parseFloat(result);
					return isNaN(result) ? 0 : result;
				default:
					return result;
			}
		} else {
			this.element.value = v;
			return this;
		}
	}
});

function getViewType(view){
	if(view instanceof UIView){
		return getViewType(view.element);
	} else if(view instanceof Element && view.nodeType === 1){
		var typeName = (CONFIGURE.prefix ? CONFIGURE.prefix + '-' : '') + 'type';
		return view.getAttribute(typeName);
	}
	return null;
}

UIModel.createRect = function(){
	var model = new UIModel({
		left: 0, top: 0, width: 50, height: 50
	});
	model.defineGetter('width', function(val){
		return Math.max(0, val || 0);
	});
	model.defineGetter('height', function(val){
		return Math.max(0, val || 0);
	});
	return model;
};


var CORE = {
	View: UIView,
	InputView: UIInputView,
	Model: UIModel,
	Listener: Listener,
	Controller: Controller,
	SchemaController: UISchemaController,
	CSSController: UICSSController,
	InputController: UIInputController
};

var MVC = {};
MVC.CONFIGURE = CONFIGURE;
MVC.core = CORE;
MVC.prefixed = prefixed;
MVC.createElement = createElement;
MVC.getAttribute = function(dom, name, noPrefix){
	if(!noPrefix){
		name = prefixed(name);
	}
	return dom.getAttribute(name);
};

function CenterScale(el, width, height, fn){
	var radio = width / height;
	el = util.$(el);
	var parent = el.parentNode;

	el.style.width = width + 'px';
	el.style.height = height + 'px';

	function cal(){
		var W = parent.offsetWidth;
		var H = parent.offsetHeight;
		var r = Math.min(W / width, H / height);
		r *= 0.9

		el.style.left = (W - width) * .5 + 'px';
		el.style.top = (H - height) * .5 + 'px';
		el.style.transform = 'scale(' + r + ',' + r + ')';

		util.isFunction(fn) && fn(r);

	}

	cal();
	util.addEvent(window, 'resize', cal);
}

MVC.CenterScale = CenterScale;

(host || window).mvc = MVC;

})(window)